[Chapter 19] V8 m4 Configuration
Author: Bryan Costales & Eric Allman
Date: 1999-01-06T19:10:22Z

#

| Chapter 19 |

# 19. V8 m4 Configuration

Contents:
The m4 Preprocessor
Build with m4
The Minimal mc File
m4 Macros by Function
Pitfalls
Alphabetized m4 Macros

V8 sendmail provides an easy way to create a custom configuration file for your site. In the cf subdirectory of the V8 sendmail source distribution you will find a file named README. It contains easy-to-understand, step-by-step instructions that allow you to create a custom configuration file for your site. This chapter supplements that file.

## 19.1 The m4 Preprocessor

Creating a configuration file with m4(1) is simplicity itself. The m4(1) program is a macro preprocessor that produces a sendmail configuration file by processing a file whose name ends in .mc (for macro configuration). That is, it processes (reads) its input and gathers definitions of macros, then replaces those macros with their values and outputs the result.

This use of macros is much the same as that described in Section 7.1, "Overview", except that m4's rules are different. With m4, macros are defined (given values) like this:

>     define(macro,value)

Here, the `macro` is a symbolic name that you will use later. Legal names must begin with an underscore or letter and may contain letters, digits, and underscores. The `value` can be any arbitrary text. A comma separates the two, and that comma can be surrounded with optional whitespace.

There must be no space between the `define` and the left parenthesis. The definition ends with the right parenthesis.

To illustrate, consider this one-line m4 source file named /tmp/x:

>                input text to be converted

>     define(A,B)A

>     the m4 definition

When m4 is run to process this file, the output produced shows that `A` (the input) is redefined to become `B`:

>     % m4 /tmp/x
>     B

### 19.1.1 m4 is greedy

The m4 program is greedy. That is, if a `macro` is already defined, its value will replace its name in the second declaration. Consider this input file:

>     define(A,B)
>     define(A,C)
>     A B

Here, the first line assigns the value `B` to the macro named `A`. The second line notices that `A` is a define macro, so m4 replaces that `A` with `B` and then defines `B` as having the value `C`. The output of this file, after processing with m4, will be:

>     C C

To prevent this kind of greedy behavior (and to prevent the confusion it can create), you may quote an item to prevent m4 from interpreting it. zzz You quote with m4 by surrounding each item with left and right single quotes:

>     define(A,B)
>     define(`A',C)
>     A B

Here, the first line defines `A` as `B` as before. But the second line no longer sees `A` as a macro. Instead, the single quotes allow `A` to be redefined as `C`. So the output is now:

>     C B

Although it is not strictly necessary, we recommend that all `macro` and `value` pairs be quoted. The above should generally be expressed like this:

>     define(`A',`B')
>     define(`A',`C')
>     A B

This is the form that we use when illustrating m4 throughout this book.

### 19.1.2 m4 and dnl

Another problem with m4 is that it replaces its commands with empty lines. The above `define` commands, for example, will actually print like this:

>      a blank line
>                                 a blank line
>     A B

To suppress this insertion of blank lines, you can use the special m4 command `dnl` (for Delete through New Line). That command looks like this:

>     define(`A',`B')dnl
>     define(`A',`C')dnl
>     A B

You can use `dnl` to remove blank lines where they might prove inconvenient or unsightly in a configuration file.

### 19.1.3 m4 and arguments

When an m4 macro name is immediately followed by a right parenthesis, it is treated like a function call. Arguments given to it in that role are used to replace `$``digit` expressions in the original definition. For example, suppose the macro CONCAT is defined like this:

>     define(`CONCAT',`$1$2$3')dnl

and then later used like this:

>     CONCAT(`host', `.', `domain')

The result will be that `host` will replace `$1`, the dot will replace `$2`, and the `domain` will replace `$3`, all jammed tightly together just as ``$1$2$3'` were:

>     host.domain

Macro arguments are used to create such techniques as FEATURE() and OSTYPE(), which are described later in this chapter.

### 19.1.4 m4 diversions

One of the m4 program's strengths is its ability to divide its input into different parts and to later reassemble them in a more logical fashion. Consider, for example, the desire to output all options together. One way to do this is with the m4 program's `divert` and `undivert` commands, for example,

>     divert(1)dnl
>     O ALIASFILE=/etc/aliases
>     divert(2)dnl
>     Pfirst-class=0
>     divert(1)dnl
>     O OperatorChars=.:%@!^/[]+
>     undivert(1)dnl
>     undivert(2)dnl

Here, the `divert(1)` causes all subsequent lines (up to the next `divert` or next `undivert`) to be held in a buffer numbered one. Buffer one will hold all the options. The second `divert` switches to buffer two, which is used to hold priorities. The third `divert` switches back to buffer one.

The `undivert(1)` causes all the options gathered in buffer one to be output, and the `undivert(2)` causes the priorities to be output. The result looks like this:

>     O ALIASFILE=/etc/aliases
>     O OperatorChars=.:%@!^/[]+
>     Pfirst-class=0

The diversions used by sendmail's m4 technique are listed in Table 19.1. In general, the macros listed should be used in place of diversion numbers because the meaning of those numbers may be changed in future versions of sendmail.

Table 19.1: m4 Diversions Used and Reserved by sendmail
divert | Description
--- | ---
(-1) | Internal to m4(1), tells it ignore all lines that follow
(0) | Internal to m4(1), tells it to stop diverting and to output immediately
(1) | Local host detection and resolution with LOCAL_NET_CONFIG (see Section 19.6.37, LOCAL-NET-CONFIG)
(2) | Rule set 3 (via 96) additions with LOCAL_RULE_3 (see Section 19.6.35, LOCAL-RULE-3)
(3) | Rule set 0 (via 98) additions with LOCAL_RULE_0 (see Section 19.6.32, LOCAL-RULE-0)
(4) | Rule set 0 UUCP additions (Section 19.4.6, "UUCP")
(5) | Locally interpreted names (overrides $R) with LOCAL_USER (Section 19.6.38, LOCAL-USER)
(6) | Local configuration (at top of file) with LOCAL_CONFIG (see Section 19.6.30, LOCAL-CONFIG)
(7) | Delivery agent definitions with MAILER (see Section 19.3.2, "MAILER()") and MAILER_DEFINITIONS (see Section 19.6.40, MAILER-DEFINITIONS)
(8) | unused
(9) | Rule sets 1 and 2 with LOCAL_RULE_1 and LOCAL_RULE_2 (see Section 19.6.34, LOCAL-RULE-2), rule set 5, and LOCAL_RULESETS (see Section 19.6.36, LOCAL-RULESETS)

18.8 Alphabetized Reference |  | 19.2 Build with m4